package com.swdegao.iot.netty.server.channelhandler;

import com.alibaba.fastjson.JSON;
import com.swdegao.iot.SwdegaoIotMgtServerApplication;
import com.swdegao.iot.netty.entity.*;
import com.swdegao.iot.netty.service.impl.TerminalsServiceImpl;
import com.swdegao.iot.netty.utils.*;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.UUID;

/**
 * @Author:zt
 * @Date: Created in 8:20 2018\10\26 0002
 */

public class ParseMessgaeHandler extends SimpleChannelInboundHandler<Object>{

	// 定义一个全局的记录器，通过LoggerFactory获取
	private final static Logger logger = LoggerFactory.getLogger(ParseMessgaeHandler.class);

	@Autowired
	private RedisUtils redisUtils = SwdegaoIotMgtServerApplication.getBean(RedisUtils.class);
	static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

	private HandlerMessage hm = new HandlerMessage();
	@Autowired
	private TerminalsServiceImpl ts = SwdegaoIotMgtServerApplication.getBean(TerminalsServiceImpl.class);

	@SuppressWarnings("unused")
	private String tempTime = null;//用于比较心跳是否超时
	private Boolean flag = true;
	private String imei = "";
	private String channelUUID = "";
	@Override
	protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { // (4)
		System.out.println("进入德高TCP端口 8210");
		ByteBuf in = (ByteBuf)msg;// 80 23 00 81 383630313934303332393334363036 40 71 0d 0a
		//System.out.println(in.toString());

		byte[] barray = new byte[in.readableBytes()];

		in.getBytes(0,barray);
	       //将byte[]转成字符串用于打印
		//System.out.println(new String(barray));


		/*byte[] b = ByteBufUtil.getBytes(in);
		System.out.println(new String(b));*/
		ByteBuf bodyAll = in.duplicate();
		String ChannelID = null;
		try {
			if (DataValida.ValidateHead(in.getByte(0))) {// 验证帧头:0x80
				byte[] byteChannel = DataResction.ResctionIMEI(in);
				ChannelID = new String(byteChannel);
				imei = ChannelID;
                logger.info("当前 IMEI:" + imei);
				// 更换连接ID
				if (!Const.hasChannelID(ChannelID)) {
					//原版本
//					String realChannelID = Const.isChannel(ctx.channel());
//					Const.ChangeClientId(realChannelID, ChannelID);

					//测试1 - 将循环合并
					//Const.isChannelAndChangeClientId(ctx.channel(),ChannelID);

					//测试2 - 将循环合并
					Const.isChannelAndChangeClientIdByUUID(channelUUID,ChannelID);//直接根据channelID获取channel，重新替换

					// 记录上线记录
					//onoffLineAction(ChannelID, Parameter.online_des,1);// 记录上线
					//onoffLineForCenter(ChannelID,1);
				}
				// 检查重复链接ID 不同实例 切换实例
				if (Const.ischangeChannelID(ChannelID, ctx.channel())) {
					logger.info("Const.ischangeChannelID,ChannelID " +ChannelID);
					Const.changeChannel(ChannelID, ctx.channel());
					// 记录重复上线记录
					//onoffLineAction(ChannelID, Parameter.reonline_des,0);// 记录下线
					//onoffLineForCenter(ChannelID,0);
				}
				// 数据域
				ByteBuf recevieMsg = Unpooled.wrappedBuffer(DataResction.ResctionBody(bodyAll));
				// 数据类型:0x81,0x82,0x83,0x84
				Byte type = DataResction.ResctionDataType(in.getByte(3));
				hm.setActionTpye(type);
				hm.setImei(ChannelID);
				hm.setRecevieMsg(recevieMsg);

				//走推送逻辑
				//Byte type = msg.getActionTpye();
				//数据类型判断
		        switch (type){
		            case (byte) 0x81:
		            	//注册包
		            	hm.setSendMsg(GetSuccessSignal(hm.getImei(), type, true));
		            	ChannelFuture cf = ctx.writeAndFlush(hm.getSendMsg());

		            	int heartbeat_registered_num = 0;
		            	if(redisUtils.hmhave(Parameter.imei_info,ChannelID)){
		            		//已经注册成功过
		            		ImeiInfo imeiInfo = JSON.parseObject(redisUtils.hmGet(Parameter.imei_info,ChannelID).toString(), ImeiInfo.class);
			            	heartbeat_registered_num = imeiInfo.getHeartbeat_registered_num();
		            	}

		            	cf.addListener(new ChannelFutureListener() {
							@Override
							public void operationComplete(ChannelFuture arg0) throws Exception {
								// 检查操作的状态
				                if (!cf.isSuccess()) {
				                	if(flag) {
				                		//2021-11-22 增加等待```
				                		Thread.sleep(30);
				                		hm.setSendMsg(GetSuccessSignal(hm.getImei(), type, true));
				                		ChannelFuture cfc = ctx.writeAndFlush(hm.getSendMsg());
				                		flag = cfc.isSuccess();
				                	}

				                }
							}
						});
		            	if(!flag || heartbeat_registered_num>1){
		            		//前两次发送响应失败或者设备端发送多次注册包，再进行一次注册响应回复。
		            		Thread.sleep(30);
		            		hm.setSendMsg(GetSuccessSignal(hm.getImei(), type, true));
		            		ctx.writeAndFlush(hm.getSendMsg());
		            	}
		                break;
		            case (byte) 0x82:
		            	//心跳包
		            	hm.setSendMsg(GetSuccessSignal(hm.getImei(), type, true));
		            	ctx.writeAndFlush(hm.getSendMsg());
		                break;
		            case (byte) 0x83:
		            	//数据包
		                break;
		            case (byte) 0x84:
		            	//时间同步包
		            	hm.setSendMsg(ToDeviceClockMsg(hm.getImei(), type));
		            	ctx.writeAndFlush(hm.getSendMsg());
		            	//存储问题答案
						//redisUtils.lPush(msg.getImei()+Parameter.imei_info_downdata,JSON.toJSONString(msg.getSendMsg()));//存储所有updata数据
		                break;
		            case (byte) 0x85:
		            	//正对与主动上报的数据回复
		            	ByteBuf Sendbuf = ToDeviceClockMsg(hm.getImei(), type);
		            	hm.setSendMsg(Sendbuf);

		            	//原方法
//		            	ByteBuf Sendbuf2 = hm.getSendMsg();

						//方法一
						ByteBuf Sendbuf2 = Unpooled.buffer();
						Sendbuf2.writeBytes(hm.getSendMsg());
						ChannelFuture cfxr = ctx.writeAndFlush(Sendbuf2);

						//监听回调
//						cfxr.addListener(new ChannelFutureListener() {
//							@Override
//							public void operationComplete(ChannelFuture arg0) {
//								if(!arg0.isSuccess()){
//									logger.info("isSuccess:"+ arg0.isSuccess() +" isDone:"+ arg0.isDone()+" cause:"+ arg0.cause().getMessage());
//								}
////									logger.info("isSuccess:"+ arg0.isSuccess() +" imei:"+ hm.getImei() + " operationComplete:" + System.currentTimeMillis());
//
//							}
//						});

		            	//redisUtils.lPush(msg.getImei()+Parameter.imei_info_downdata,JSON.toJSONString(msg.getSendMsg()));//存储所有updata数据
		                break;
		            case (byte) 0x86:
		            	//设备下线
		            	inactiveAction(ctx);
		                break;

		        }

				// ReferenceCountUtil.release(recevieMsg);
				ctx.fireChannelRead(hm);
			} else {
				// 验证不通过关闭channel
				// ctx.writeAndFlush(CallBackMessage.ERROR.duplicate());
				ctx.close();
			}
		}catch(Exception e){
			logger.error(ChannelID+ ":" +e.getMessage()+ "channelUUID:" + channelUUID + "imei:" + imei,e);
		} finally {
			// ReferenceCountUtil.release(in);
			//logger.error(ChannelID+"ParseMessgaeHandlerfinally");
		}
	}

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception { // (5)
//		logger.info("TCP通道已经连接");
//		Channel incoming = ctx.channel();
		channelUUID = String.valueOf(UUID.randomUUID());
		Const.add(channelUUID, ctx.channel());
		channels.add(ctx.channel());
		//logger.info("client " + incoming.remoteAddress() + "上线");
	}

	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception { // (6)
		inactiveAction(ctx);
		// 记录下线
		//logger.info("Disconnected client " + ctx.channel().remoteAddress() + "掉线" + channelId);
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (7)
		Channel incoming = ctx.channel();
		//logger.info("client " + incoming.remoteAddress() + "异常" + cause.getMessage());
		// 当出现异常就关闭连接
		cause.printStackTrace();
		// ctx.close();
	}

	/**
	 * 上线操作
	 *
	 * @param imei
	 * @param channel
	 */
	private void onoffLineAction(String imei, String type,int status) {
		OnOffData onOff = new OnOffData();
		onOff.setImei(imei);
		onOff.setCreatetime(System.currentTimeMillis());
		onOff.setAction(type);
		redisUtils.lPush(imei + Parameter.on_off_info, JSON.toJSONString(onOff));// 存储所有updata数据
	}
	/**
	 * 上线下线操作
	 *
	 * @param imei
	 * @param channel
	 */
	@SuppressWarnings("unused")
	private void onoffLineForCenter(String imei, int status) {
		Terminals terminal = new Terminals();
		terminal.setImei(imei);
		terminal.setOnlinestatus(status);
		//ts.updateTerminalOnlineStatus(terminal);
	}
	/**
	 * 下线完整操作
	 */
	private void inactiveAction(ChannelHandlerContext ctx){
//		String channelId = Const.getChannelIds(ctx.channel());
		String channelId = imei;
//		logger.info("imei:"+imei+ " inactiveAction:" + System.currentTimeMillis());
		try{
			if(!"".equals(channelId) && channelId.indexOf("_") == -1){
				onoffLineAction(channelId, Parameter.offline_des,0);// 记录下线
				onoffLineForCenter(channelId,0);
				//清掉redis中关于心跳记录
				ImeiInfo imeiInfo = JSON.parseObject(redisUtils.hmGet(Parameter.imei_info,channelId).toString(), ImeiInfo.class);
				imeiInfo.setHeartbeat_delay_num(0);
				imeiInfo.setHeartbeat_registered_num(0);
				redisUtils.hmSet(Parameter.imei_info, channelId, JSON.toJSONString(imeiInfo));
			}
		}catch (Exception ignored){

		}finally {
			Const.remove(ctx.channel());
			channels.remove(ctx.channel());
		}


	}

	public String getTimeNow() {
		SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		return df.format(new Date());
	}

	/**
	 * 获取超时时间
	 * @param timeNow
	 */
	public String getTimeOut(String timeNow) {
		SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Calendar cal = new GregorianCalendar();

		try {
			cal.setTime(df.parse(timeNow));
			cal.add(Calendar.SECOND, Parameter.outtime_hear);

		} catch (ParseException e) {
			e.printStackTrace();
		}
		return df.format(cal.getTime());
	}

	/**
	 * 比较超时时间，是否超时
	 */
	public int CompareOutTime(String timeNow,String timeAfter, SimpleDateFormat timestampFormat)
	{
        if(timeNow.equals(timeAfter)) // same
            return 1;
        try {
            Date timesN = timestampFormat.parse(timeNow);
            Date timesA = timestampFormat.parse(timeAfter);
            if(timesA.after(timesN)) // time 1 is newer
                return 1;
            else return -1;
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return 1;
	}

	/**
	 *
	 * @param imei
	 * @param type
	 * @return
	 */
	public ByteBuf ToDeviceClockMsg(String imei,byte type)
     {
		 SimpleDateFormat df = new SimpleDateFormat("yyMMddHHmmss");
		 String date = df.format(new Date());
		 byte[] temp2_ = ConvertUtils.getDateFromStr(date, 6);
         byte[] msg = new byte[22 + temp2_.length];
         msg[0] = (byte) 0x80;
         msg[1] = 0x28;
         msg[2] = 0;
         msg[3] = type;

         byte[] temp = imei.getBytes();
         System.arraycopy(temp, 0, msg, 4, 15);//类型、imei

         //byte[] temp = ConvertUtils.getByte(Integer.parseInt(imei), 15);
		 //System.arraycopy(temp, 0, msg, 4, 15);
         System.arraycopy(temp2_, 0, msg, 19, 6);
         msg[msg.length - 3] = ConvertUtils.getTotalForByte(msg);
         msg[msg.length - 2] = 0x0d;
         msg[msg.length - 1] = 0x0a;
         return Unpooled.wrappedBuffer(msg);
     }


	public ByteBuf GetSuccessSignal(String imei,byte type,boolean suc) {
		byte[] msg = new byte[23];
        msg[0] = (byte) 0x80;
        msg[1] = 0x23;
        msg[2] = 0;
        msg[3] = type;
        byte[] temp = imei.getBytes();
        System.arraycopy(temp, 0, msg, 4, 15);//类型、imei
        msg[19] = (byte)(suc ? 0x73 : 0x74);//应答状态
        msg[20] = ConvertUtils.getTotalForByte(msg);//校验和
        msg[21] = 0x0d;
        msg[22] = 0x0a;
		return  Unpooled.wrappedBuffer(msg);
	}
	/**
	 * 预置命令
	 * @param imei
	 * @param presetCommand
	 * @return
	 * @throws DecoderException
	 */
	public ByteBuf GetPresetCommand(String imei, PresetCommand presetCommand) throws DecoderException {
		byte[]  downData = Hex.decodeHex(presetCommand.getDownData());
		int msg_length = 22 + downData.length;
		int msg_length_h = msg_length/100;
		int msg_length_l = msg_length -(msg_length/100)*msg_length_h;
		byte[] msg = new byte[msg_length];
        msg[0] = (byte) 0x80;
        msg[1] = (byte)Integer.parseInt(msg_length_l+"", 16);
        msg[2] = (byte)Integer.parseInt(msg_length_h+"", 16);
        msg[3] = (byte) 0x86;
        byte[] temp = imei.getBytes();
        System.arraycopy(temp, 0, msg, 4, 15);//类型、imei
        System.arraycopy(downData, 0, msg, 19, downData.length);//类型、imei
        msg[msg_length-3] = ConvertUtils.getTotalForByte(msg);//校验和
        msg[msg_length-2] = 0x0d;
        msg[msg_length-1] = 0x0a;
		return  Unpooled.wrappedBuffer(msg);
	}


}
